import createQrCodeImg from './qr'
// 二维码
interface options { 
  typeNumber?: number
  errorCorrectLevel?: 'L' | 'M' | 'Q' | 'H'
  size?: number
}
export const CreateQrCode = (text:string, options:options = {}) => { 
  return createQrCodeImg(text, options)
}
/****************************************************************************** */
/****************************************************************************** */
/************************  正则及其验证方法  ********************************** */
/****************************************************************************** */
/****************************************************************************** */
/****************************************************************************** */
// 手机号
export const Mobile = /^1[3456789]\d{9}$/
export const IsMobile = (value: string) => Mobile.test(value)
// 座机，必须0开头且包含短横线
export const StudioCamera = /^0\d{2,3}-\d{6,8}$/
export const IsStudioCamera = (value: string) => StudioCamera.test(value)
// 邮箱
export const Email = /^\w+(\.)?(\w+)?@[0-9a-z]+(\.[a-z]+){1,3}$/
export const IsEmail = (value: string) => Email.test(value)
// 身份证
export const IdCard = /^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/
export const IsIdCard = (value: string) => IdCard.test(value)
// 数字
export const Digit = /^\d+$/
export const IsDigit = (value: string|number) => Digit.test(value as string)
// 数字字母
export const DigitLetter = /^[A-Za-z0-9]+$/
export const IsDigitLetter = (value: string|number) => DigitLetter.test(value as string)
// 中文
export const Chinese = /^[\u4e00-\u9fa5]+$/
export const IsChinese = (value: string) => Chinese.test(value)
// 中文，英文，数字，下滑线
export const CnEnNuLine = /^[\u4e00-\u9fa5A-Za-z0-9_]+$/
export const IsCnEnNuLine = (value: string) => CnEnNuLine.test(value)
// 英文
export const English = /^[A-Za-z]+$/
export const IsEnglish = (value: string) => English.test(value)
// 大写英文
export const CapitalEnglish = /^[A-Z]+$/
export const IsCapitalEnglish = (value: string) => CapitalEnglish.test(value)
// ipv4
export const Ipv4 = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
export const IsIpv4 = (value: string) => Ipv4.test(value)
// http,https地址
export const Url = /^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=]+$/
export const IsUrl = (value: string) => Url.test(value)
// 邮编
export const PostCode = /^[0-8]\d{5}$/
export const IsPostCode = (value: string) => PostCode.test(value)
// 社会信用代码编码规则
export const SocialCreditCode = /[0-9A-HJ-NPQRTUWXY]{2}\d{6}[0-9A-HJ-NPQRTUWXY]{10}/
export const IsSocialCreditCode = (value: string) => SocialCreditCode.test(value)


/****************************************************************************** */
/****************************************************************************** */
/***************************  object类型  ************************************* */
/****************************************************************************** */
/****************************************************************************** */
/****************************************************************************** */

/**
 * sessionStorage
 */
export const sessions = {
  get(key: string) {
    const data = sessionStorage[key];
    if (!data || data === "null") {
      return null;
    }
    return JSON.parse(data).value;
  },
  set<T>(key: string, value: T) {
    const data = {
      value
    };
    sessionStorage[key] = JSON.stringify(data);
  },
  // 删除
  remove(key: string) {
    sessionStorage.removeItem(key);
  },
  // 清除全部
  clear() {
    sessionStorage.clear();
  }
};

/**
 * localStorage
 */
export const local = {
  get(key: string) {
    const data = localStorage[key];
    if (!data || data === "null") {
      return null;
    }
    return JSON.parse(data).value;
  },
  set<T>(key: string, value: T) {
    const data = {
      value
    };
    localStorage[key] = JSON.stringify(data);
  },
  // 删除
  remove(key: string) {
    localStorage.removeItem(key);
  },
  // 清除全部
  clear() {
    localStorage.clear();
  }
};


/****************************************************************************** */
/****************************************************************************** */
/******************************  函数  **************************************** */
/****************************************************************************** */
/****************************************************************************** */
/****************************************************************************** */
/**
 * 时间格式化
 * @param timeTemp 时间戳，毫秒
 * @param fmt 格式化结构
 */
export const DateFormat = (timeTemp: number, fmt: string = 'YYYY-MM-DD hh:mm:ss') => {
  if ((!timeTemp && timeTemp !== 0) || typeof timeTemp !== 'number') {
    return ''
  }
  const ThisDate: any = new Date(timeTemp)
  const getYearWeek = () => { // 计算年度周数
    const firstDay: any = new Date();  // 该年的第一天
    const year = ThisDate.getFullYear();
    firstDay.setYear(year);
    firstDay.setMonth(0);
    firstDay.setDate(1);  // 设置成这年的一月一号
    const yearDays: any = Math.ceil((ThisDate - firstDay) / (24 * 60 * 60 * 1000));  // 距离第一天共有多少天
    let days = yearDays + firstDay.getDay() + 1;  // 假如今年一月一号是星期三，第一周就已经过去了三天，加上西方认为星期天是一周的第一天
    let weeks = Math.ceil(days / 7);  // 获得周数
    return { weeks, yearDays };
  }
  const getMonthWeek = () => {
    const w = ThisDate.getDay();
    const d = ThisDate.getDate();
    return Math.ceil(
      (d + 6 - w) / 7
    );
  };
  let o: any = {
    'M+': ThisDate.getMonth() + 1, // 月份 
    'D+': ThisDate.getDate(), // 日 
    'h+': ThisDate.getHours(), // 小时 
    'm+': ThisDate.getMinutes(), // 分 
    's+': ThisDate.getSeconds(), // 秒 
    'q+': Math.floor((ThisDate.getMonth() + 3) / 3), // 季度 
    'S': ThisDate.getMilliseconds(), // 毫秒 
    'w': getMonthWeek(), // 当月第几周
    'WW': getYearWeek().weeks, // 当年第几周
    'd': ThisDate.getDay(), // 周几
    'n': getYearWeek().yearDays + 1, // 今年第几天
  };
  if (/([yY]+)/.test(fmt)) {
    fmt = fmt.replace(RegExp.$1, (ThisDate.getFullYear() + '').substr(4 - RegExp.$1.length));
  }
  for (let k in o) {
    if (new RegExp('(' + k + ')').test(fmt)) {
      let sk = RegExp.$1.length === 1 ? o[k] : ('00' + o[k]).substr(('' + o[k]).length);
      fmt = fmt.replace(RegExp.$1, sk);
    }
  }
  return fmt;
};

/**
 * 数字千分位
 * @param value 值
 * @param float 小数位是否要转，默认false
 */
export const NumberDot = (value: number, float: boolean = false) => {
  if ((!value && value !== 0) || typeof value !== 'number') {
    return ''
  }
  let str = value.toString();
  let reg = str.indexOf('.') > -1 ? /(\d)(?=(\d{3})+\.)/g : /(\d)(?=(?:\d{3})+$)/g;
  if (float) {
    return str.replace(/\B(?=(\d{3})+(?!\d))/g, ',')
  }
  return str.replace(reg, '$1,');
}

/** 
 * 数据脱敏
 * @param val 要替换的数据
 * @param start 头部显示位数，默认3
 * @param end 尾部显示位数，默认4
*/
export const ReplaceStar = (value: string, start: number = 3, end: number = 4) => {
  if (!value || typeof value !== 'string') { // 字符串为空直接返回
    return value;
  }
  if (value.length <= (start + end)) {
    console.error(`replaceStar: value.length less than (start + end)total, the value (${value})`)
    return value
  }
  let str = '*';
  str = str.repeat(value.length - start - end); // * 重复次数
  let re = new RegExp('(.{' + start + '}).*(.{' + end + '})', ''); // 动态的正则验证
  return value.replace(re, '$1' + str + '$2'); // 替换
};

/**
 * 判断是否带有小数位
 * @param number 要判断的值
 * @param maxLen 最多保留小数位
 */
export const NumberIsFloat = function (number: number, maxLen?: number) {
  if (!isNaN(number) && number.toString().indexOf('.') > -1) {
    if (maxLen) {
      return number.toString().split('.')[1].length > maxLen ? false : true;
    }
    return true;
  }
  return maxLen ? true : false;
};

/**
 * 数字转万，千分位
 * @param value 要转的数值,
 * @param float 保留小数位
 */
export const NumberWan = (value: number, float: number = 2) => {
  if ((!value && value !== 0) || typeof value !== 'number') {
    return ''
  }
  if (value < 10000) {
    let str = value.toString();
    let reg = str.indexOf('.') > -1 ? /(\d)(?=(\d{3})+\.)/g : /(\d)(?=(?:\d{3})+$)/g;
    return str.replace(reg, '$1,');
  } else {
    let str = Number((value / 10000).toFixed(float)).toString();
    let reg = str.indexOf('.') > -1 ? /(\d)(?=(\d{3})+\.)/g : /(\d)(?=(?:\d{3})+$)/g;
    return str.replace(reg, '$1,');
  }
}


/**
 * 离屏文件选择
 * @param fileName 上传文件名
 * @param accept 上传文件类型
 * @param formData 返回的数据需要转为formData
 * @param callback 选中后回调
 */
interface config {
  fileName?: string
  accept?: string
  multiple?: boolean
  formData?: boolean
}
export const OutScreenFileChoose = (config: config) => {
  const inputObj = document.createElement('input');
  inputObj.setAttribute('type', 'file');
  inputObj.setAttribute('name', 'file');
  config?.multiple ? inputObj.setAttribute('multiple', 'true') : '';
  inputObj.setAttribute('accept', config?.accept || 'image/*');
  inputObj.click();
  return new Promise((resolve, reject) => {
    try {
      inputObj.addEventListener('change', (e: any) => {
        const files = e.target.files
        let forms: any = []
        if (config?.formData) {
          for (let item of files) {
            let form = new FormData();
            form.append(config?.fileName || 'file', item);
            forms.push(form)
          }
        } else {
          forms = files
        }
        resolve(forms)
      });
    }
    catch {
      resolve('')
    }
  })
};

/**
 * 复制
 * @param value 要复制的内容
 */
export const Copy = (value: any) => {
  try {
    const input = document.createElement('input');
    document.body.appendChild(input);
    input.setAttribute('value', value);
    input.select();
    if (document.execCommand('copy')) {
      document.execCommand('copy');
      document.body.removeChild(input);
      return true
    }
    document.body.removeChild(input);
    return false
  }
  catch {
    return false
  }

}

/***
 * 深拷贝
 * @param value 要拷贝的值
 */
export const DeepClone = (value: any, map = new WeakMap()) => {
  if (value.constructor === Date) {
    return new Date(value)
  }
  if (value.constructor === Set) {
    return new Set(value)
  }
  if (value.constructor === Map) {
    return new Map(value)
  }
  if (value.constructor === RegExp) {
    return new RegExp(value)
  }
  if (typeof value !== 'object' || value === null) {
    return value
  }
  const prototype = Object.getPrototypeOf(value)
  const description = Object.getOwnPropertyDescriptors(value)
  const object = Object.create(prototype, description)
  map.set(value, object)
  Reflect.ownKeys(value).forEach(key => {
    if (typeof key !== object || key === null) {
      object[key] = value[key]
    } else {
      const mapValue = map.get(value)
      mapValue ? (object[key] = map.get(value)) : (object[key] = DeepClone(value[key]))
    }
  })
  return object
}


/** 
 * file转base64
 * @param file 选择的文件
*/
export const FileToBase64 = (file: any) => {
  return new Promise((resolve, reject) => {
    try {
      let reader = new FileReader();
      reader.onload = (evt: any) => {
        let base64 = evt.target.result;
        resolve(base64)
      };
      reader.readAsDataURL(file);
    }
    catch {
      resolve('')
    }
  })
};


/**
 * 删除对象中的空（null,undefined,''）属性
 * @param obj
 * @returns
 */
export const DeleteEmptyObj = (obj: any) => {
  const result: any = {};
  if (obj === null || obj === undefined || obj === '') return result;
  for (const key in obj) {
    if (IsObject(obj[key])) {
      result[key] = DeleteEmptyObj(obj[key]);
    } else if (obj[key] !== null && obj[key] !== undefined && obj[key] !== '') {
      result[key] = obj[key];
    }
  }
  return result;
}

/**
 * 资源文件下载到本地：
 * 这是一个汇总的下载方式，主要是处理一些图片下载，文档类型下载，
 * 你也可以使用XMLHttpRequest单独下载文档类型文件，
 * 或者使用XMLHttpRequest + WebImageToBase64下载图片类型文件
 * 但是我建议使用【FileDownload】这个总入口下载图片、文档类型文件
 * @param link 需要下载的文档，图片地址
 * @param name 下载后的文件名，默认是源文件的名称
 */

export const FileDownload = async (link: string, name?: string) => {
  const fileName = name ? name : decodeURIComponent(link.split('?')[0].split('.').reverse()[1].split('/').reverse()[0])
  const type = link.split('.').reverse()[0].split('?')[0].toLowerCase();
  if (['jpg', 'png', 'jpeg', 'gif', 'webp'].includes(type)) { // 图片
    let image: any = await WebImageToBase64(link);
    if (image) {
      XMLHttpRequest(image, `${fileName}.${type}`, 'image');
      return
    }
    console.error('WebImageToBase64 back value error')
  } else { // 非图片
    XMLHttpRequest(link, fileName, 'file');
  }
}

/**
 * 下载文件，资源文件下载FileDownload有调用
 * @param link 文件下载地址，图片为base64或ie下的blob
 * @param name 文件保存的名称
 * @param type 文件类型，图片：image，非图片：file
 */
export const XMLHttpRequest = (link: string, name: string, type: string) => {
  const x = new window.XMLHttpRequest();
  x.open('GET', link, true);
  x.responseType = 'blob';

  x.onload = function () {
    const blob = x.response;
    const url = window.URL.createObjectURL(blob);
    // 判断是否是IE浏览器
    if ('msSaveBlob' in window.navigator) {
      try {
        (window.navigator as any).msSaveBlob(type === 'image' ? link : blob, name);
      } catch (e) {
        console.error(e);
      }
    } else {
      const a = document.createElement('a');
      a.href = url;
      a.download = name || '';
      a.click();
    }
  };
  x.send();
}

/**
 * 网络图片转为本地base64或blob，资源文件下载FileDownload有调用
 * @param imgUrl 网络图片地址
 */
export const WebImageToBase64 = (imgUrl: string) => {
  return new Promise((resolve, reject) => {
    try {
      const canvas = document.createElement('canvas');
      const image = new Image();
      image.setAttribute('crossOrigin', 'anonymous');
      image.src = `${imgUrl}${imgUrl.indexOf('?') > -1 ? '&' : '?'}v=${Math.random()}`;
      image.onload = async () => {
        canvas.width = image.width;
        canvas.height = image.height;
        const ctx = canvas.getContext('2d');
        ctx && ctx.drawImage(image, 0, 0, image.width, image.height);
        const ext = image.src.substring(image.src.lastIndexOf('.') + 1).toLowerCase();
        const base64 = canvas.toDataURL(`image/${ext}`);
        if ('msSaveBlob' in window.navigator) { // IE
          let blob = await Base64toBlob(base64);
          if (blob) {
            resolve(blob)
          }
          resolve('')
          console.error('Base64toBlob back value is error')
        } else { // 非IE
          resolve(base64);
        }
      };
    }
    catch {
      resolve('')
    }
  })
}
/**
 * base64转blob，主要是ie会用到，资源文件下载FileDownload有调用
 * @param base64 base64文件数据
 */
export const Base64toBlob = (base64: any) => {
  return new Promise((resolve, reject) => {
    try {
      const arr = base64.split(',');
      const bstr = atob(arr[1]);
      let n = bstr.length;
      const u8arr = new Uint8Array(n);
      while (n > 0) {
        n -= 1;
        u8arr[n] = bstr.charCodeAt(n);
      }
      const blob = new Blob([u8arr]);

      resolve(new File([blob], `${Date.now()}.${base64.match(/:(\S*);/)[1].split('/')[1]}`, { type: base64.match(/:(\S*);/)[1] }));
    }
    catch {
      resolve('')
    }
  })

}

/**
 * 防抖
 * 只执行最后一次事件
 */
let debounceTime:any = null
export const Debounce = (cb:Function,time:number = 300) => { 
  if (debounceTime) { 
    clearTimeout(debounceTime)
    debounceTime = null
  }
  debounceTime = setTimeout(() => {
    clearTimeout(debounceTime)
    debounceTime = null
    cb()
  }, time);
}

/**
 * 节流
 * 一段时间内只执行一次事件
 */
let throttleTime:any = null
export const Throttle = (cb:Function, time:number = 300) => { 
  if (throttleTime) { 
    return
  }
  throttleTime = setTimeout(() => {
    clearTimeout(throttleTime)
    throttleTime = null
    cb()
  }, time);
}
  

/****************************************************************************** */
/****************************************************************************** */
/*****************************  canvas类  *************************************** */
/****************************************************************************** */
/****************************************************************************** */
/****************************************************************************** */

/**
 * canvas图片合成
 * @param config 看下面的interface吧，忒多了
 */
interface canvasImageComposeListItem {
  type: string // 元素类型：`image`图片，`text`文本，`rect`矩形（线条），`arc`圆形，`qr`二维码
  x: number // X轴坐标
  y: number // Y轴坐标
  content: string // image：图片数据，text：文字，rect及arc：不使用此字段
  width?: number // 图片、矩形（线条）、圆形宽度
  height?: number // 图片、矩形（线条）高度 
  arc?: boolean // type=image时：是否绘制圆形图片
  arcType?: 'cover' | 'scale' | 'clip' // 裁剪圆形图片模式，默认cover
  arcClipXYS?: number[] // arcType=clip时裁剪坐标及缩放等级，1为不缩放，例[20,30,1]
  color?: string // 绘制文本、矩形（线条）的颜色，默认：#000000
  font?: number // 绘制文本的字号大小，字体样式，格式示例'20px sans-serif'
  align?: 'left' | 'right' | 'center' // 绘制文本的对齐方式，默认：left
  maxWidth?: number // 绘制文本的最大宽度，文字长度超过该值会被压缩 
  globalAlpha?: number // 透明度：0~1，默认1
}
interface canvasImageCompose {
  list: canvasImageComposeListItem[] // 要渲染的数据列表
  imgType?: 'jpeg' | 'png' // 输出图像格式
  height?: number // canvas高度
  width?: number // canvas宽度
}
export const CanvasImageCompose = (config: canvasImageCompose) => {
  return new Promise((resolve, reject) => {
    try {
      if (!config || !config?.list || (config?.list as any)?.length === 0) {
        console.error('prototype: list empty!')
        resolve('')
      }
      // 初始化数据
      let Init: any = {
        imgType: config.imgType || 'jpeg',
        height: config.height || 1330,
        width: config.width || 750,
        list: config.list,
        arcWidth: 500,
        arcHeight: 500
      }
      let drawIndex: any = 0 // 绘制到第几步，相当于list的索引，用于判断是否绘制完


      if (Init.imgType === 'jpeg') { // jpeg格式的时候，需要背景白色
        Init.list.unshift(
          {
            type: 'rect',
            x: 0,
            y: 0,
            content: '',
            color: '#ffffff',
            width: Init.width,
            height: Init.height,
            globalAlpha: 1
          }
        )
      }

      // 装画布的盒子
      let CanvasBox: any = document.createElement('div')
      CanvasBox.style.display = 'none'

      // 主画布创建
      let CanvasMain: any = document.createElement('canvas')
      CanvasMain.width = Init.width
      CanvasMain.height = Init.height
      CanvasMain.setAttribute('style', `width:${Init.width * window.devicePixelRatio}px;height:${Init.height * window.devicePixelRatio}px`)
      CanvasMain.id = "CanvasMain"
      let CtxMain: any = CanvasMain.getContext('2d');

      // 圆形画布创建-只画圆
      let CanvasArc: any = document.createElement('canvas')
      CanvasArc.width = Init.arcWidth
      CanvasArc.height = Init.arcWidth
      CanvasArc.setAttribute('style', `width:${Init.arcWidth * window.devicePixelRatio}px;height:${Init.arcWidth * window.devicePixelRatio}px`)
      CanvasArc.id = "CanvasArc"
      let CtxArc: any = CanvasArc.getContext('2d');

      CanvasBox.appendChild(CanvasMain)
      CanvasBox.appendChild(CanvasArc)
      document.body.appendChild(CanvasBox)

      // 开始绘制
      let drawStart: any = async () => {
        let item = Init.list[drawIndex];
        if (item.type === 'image') { // 图片
          if (typeof item.content === 'string' && item.content?.startsWith('http')) { // 网络图片
            downloadImageH5(item);
          } else { // 本地选择图片
            item.content = await FileToBase64(item.content)
            if (item.arc) {
              drawImageArc(item);
            } else {
              drawImage(item);
            }
          }

        } else if (item.type === 'text') { // 文本
          drawText(item);
        } else if (item.type === 'rect') { // 矩形（线条）
          drawRect(item);
        } else if (item.type === 'arc') { // 圆形
          drawArc(item);
        }
      }

      // 绘制矩形
      let drawRect: any = (item: canvasImageComposeListItem) => {
        CtxMain.fillStyle = item?.color || '#000000';
        CtxMain.globalAlpha = item?.globalAlpha || 1;
        CtxMain.fillRect(item.x, item.y, item.width || 0, item.height || 0);
        checkDrawOver();
      }
      // 圆形绘制
      let drawArc: any = (item: canvasImageComposeListItem) => {
        const arcR = (item?.width || 0) / 2
        CtxMain.arc(item.x + arcR, item.y + arcR, arcR, 0, 2 * Math.PI);
        CtxMain.fillStyle = item?.color || '#000000';
        CtxMain.strokeStyle = '#ff0000';
        CtxMain.globalAlpha = item?.globalAlpha || 1;
        CtxMain.fill();
        CtxMain.closePath();
        checkDrawOver();
      }
      // 文本绘制
      let drawText: any = (item: canvasImageComposeListItem) => {
        CtxMain.fillStyle = item?.color || '#000000';
        if (item?.font) {
          CtxMain.font = item.font;
        }

        CtxMain.textAlign = item?.align || 'left';
        CtxMain.globalAlpha = item?.globalAlpha || 1;
        if (item?.maxWidth) {
          CtxMain.fillText(item.content, item.x, item.y, item.maxWidth);
        } else {
          CtxMain.fillText(item.content, item.x, item.y);
        }
        checkDrawOver();
      }
      // 下载网络图片
      let downloadImageH5: any = async (item: canvasImageComposeListItem) => {
        const base64: any = await WebImageToBase64(item.content)
        if (!base64) { // 网络图片下载失败，跳过
          checkDrawOver();
          return
        }
        item.content = base64
        if (item?.arc) {
          drawImageArc(item);
        } else {
          drawImage(item);
        }
      }
      // 圆形图片另外绘制canvas,png格式
      let drawImageArc: any = (item: canvasImageComposeListItem) => {
        CtxArc.clearRect(0, 0, Init.arcWidth, Init.arcHeight)
        CtxArc.beginPath();
        CtxArc.arc(Init.arcWidth / 2, Init.arcWidth / 2, Init.arcWidth / 2, 0, 2 * Math.PI);
        CtxArc.closePath();
        CtxArc.clip();
        const image = new Image()
        image.src = item.content
        let width = image.width
        let height = image.height
        let x = 0
        let y = 0
        if (!item?.arcType || item?.arcType === 'cover') { // 铺满
          width = Init.arcWidth
          height = Init.arcHeight
        }
        if (item?.arcType === 'scale') { // 等比缩放，完全显示
          if (image.width > image.height) {
            width = Init.arcWidth
            height = Init.arcWidth * image.height / image.width
          } else {
            width = Init.arcWidth * image.width / image.height
            height = Init.arcWidth
          }
        }
        x = (Init.arcWidth - width) / 2
        y = (Init.arcWidth - height) / 2
        if (item?.arcType === 'clip' && item?.arcClipXYS && item?.arcClipXYS.length === 3) { // 裁剪
          width = image.width * (item.arcClipXYS as any)[2]
          height = image.height * (item.arcClipXYS as any)[2]
          x = (item.arcClipXYS as any)[0]
          y = (item.arcClipXYS as any)[1]
        }
        image.style.width = `${width}px`
        image.style.height = `${height}px`

        image.onload = () => {
          CtxArc.drawImage(
            image,
            x,
            y,
            width,
            height
          );
          const base64 = (document.getElementById('CanvasArc') as any)?.toDataURL('image/png')
          item.content = base64
          drawImage(item)
        }
      }
      // 图片绘制
      let drawImage: any = (item: canvasImageComposeListItem) => {
        const image = new Image()
        image.src = item.content
        CtxMain.globalAlpha = item?.globalAlpha || 1;
        image.onload = () => {
          CtxMain.drawImage(
            image,
            item.x,
            item.y,
            item.width || image.width,
            item.height || image.height
          );
          checkDrawOver();
        }
      }

      // 判断是否绘制完
      let checkDrawOver: any = () => {
        if (drawIndex < Init.list.length - 1) { // list未画完
          drawIndex++;
          drawStart();
        } else {
          canvasImage();
        }
      }
      // 绘制到画布并生成图片
      let canvasImage: any = () => {
        const base64 = (document.getElementById('CanvasMain') as any)?.toDataURL(`image/${Init.imgType}`, 0.99)
        resolve(base64)
        drawIndex = null
        document.body.removeChild(CanvasBox)
        CanvasBox = null
        CanvasMain = null
        CtxMain = null
        CanvasArc = null
        CtxArc = null
        Init = null
        drawStart = null
        drawRect = null
        drawArc = null
        drawText = null
        downloadImageH5 = null
        drawImageArc = null
        drawImage = null
        checkDrawOver = null
        canvasImage = null
      }
      drawStart()
    } catch {
      resolve('')
    }
  })
}

/**
 * canvas涂抹裁剪
 */
interface CanvasDaubClipProps {
  image: string // 图片路径
  el?: string // 挂载的元素id名称
  width?: number
  height?: number
  radius?: number // 涂抹半径
  alpha?: number // 透明度
  handleClip?: boolean // 松手就裁剪
  shade?: string // 遮罩层的颜色（#号开头的）或者图片（base64或有效路径）
}
interface DaubInit { 
  el: any
  image: string
  radius: number
  alpha: number
  handleClip: boolean
  width: number
  height: number
  shade: any
  touchX: number
  touchY: number
}
export const CanvasDaubClip = (config: CanvasDaubClipProps) => {
  return new Promise(async (resolve, reject) => {
    try {
      if (!config?.image) {
        console.error('prototype: image is empty!')
        resolve('')
      }
      // 数据初始化
      let Init: DaubInit = {
        el: document.getElementById(config.el as any),
        image: config.image,
        radius: config?.radius || 20,
        alpha: config?.alpha || 0.6,
        handleClip: config?.handleClip || false,
        width: config?.width || 300,
        height: config?.height || 300,
        shade: config?.shade || '#000000',
        touchX: 0,
        touchY: 0
      }
      // DOM生成
      // 装画布的盒子
      let CanvasBox: any = document.createElement('div')
      CanvasBox.setAttribute('style', `
        width:${Init.width}px;
        height:${Init.height}px;
        position: relative;
        overflow: hidden
      `)

      // 图片
      let Img: any = new Image()
      Img.src = typeof Init.image === 'object' ? await FileToBase64(Init.image) as any : Init.image
      Img.setAttribute('style', `
        display: block;
      `)
      Img.onload = () => {
        const w = Img.width
        const h = Img.height
        Img.width = Init.width
        Img.height = Init.width * h / w
      }


      // 主画布创建，裁剪层
      let CanvasImage: any = document.createElement('canvas')
      CanvasImage.width = Init.width
      CanvasImage.height = Init.height
      CanvasImage.setAttribute('style', `
        width:${Init.width}px;
        height:${Init.height}px;
        position: absolute;
        left: 10000px;
        top: 0;
      `)
      CanvasImage.id = "CanvasImage"
      let CtxImage: any = CanvasImage.getContext('2d');

      // 遮罩层，涂抹层
      let CanvasShade: any = document.createElement('canvas')
      CanvasShade.width = Init.width
      CanvasShade.height = Init.height
      CanvasShade.setAttribute('style', `
        width:${Init.width}px;
        height:${Init.height}px;
        position: absolute;
        left: 0;
        top: 0;
        z-index: 9999
      `)
      CanvasShade.id = "CanvasShade"
      let CtxShade: any = CanvasShade.getContext('2d');


      // 输出层，保证输出的图像大小和涂抹的一样
      let CanvasOut: any = document.createElement('canvas')
      CanvasOut.id = "CanvasOut"
      let CtxOut: any = CanvasOut.getContext('2d');

      CtxShade.save();
      CtxImage.save();

      CanvasBox.appendChild(Img)
      CanvasBox.appendChild(CanvasImage)
      CanvasBox.appendChild(CanvasShade)
      CanvasBox.appendChild(CanvasOut)
      if (Init.el) {
        Init.el.appendChild(CanvasBox)
      } else {
        document.body.appendChild(CanvasBox)
      }

      // 绘制蒙层
      let createShade: any = async () => {
        CtxShade.rect(0, 0, Init.width, Init.height);
        if (Init.shade.startsWith('#')) {
          CtxShade.fillStyle = Init.shade;
          fillShade()
        } else {
          const image = new Image()
          image.src = Init.shade.startsWith('http') ? await WebImageToBase64(Init.shade) as any : Init.shade
          image.onload = () => {
            CtxShade.drawImage(
              image,
              0,
              0,
              Init.width,
              Init.height
            );
            fillShade()
          }
        }
      }
      let fillShade: any = () => {
        CtxShade.globalAlpha = Init.alpha
        CtxShade.fill();
        CtxShade.globalCompositeOperation = 'destination-out';
        CtxImage.beginPath();
        CtxShade.beginPath();
      }
      createShade()
      // 触摸开始
      let isMouseDown = false
      const isMobile = /mobile|android|webos|iphone|ipad|phone/i.test(window.navigator.userAgent.toLowerCase())
      let touches:any = []
      CanvasBox.addEventListener(isMobile ? 'touchstart' : 'mousedown', (e: any) => {
        isMouseDown = true
        e.preventDefault();

        if (!Init.image) {
          return;
        }
        const offset = e.target.getBoundingClientRect()
        if (isMobile) {
          Init.touchX = e.touches[0].clientX - offset.left;
          Init.touchY = e.touches[0].clientY - offset.top;
        } else {
          Init.touchX = e.clientX - offset.left;
          Init.touchY = e.clientY - offset.top;
        }
        touches = [Init.touchX, Init.touchY]

        CtxShade.beginPath();
        CtxShade.fillStyle = 'rgba(0,0,0,1)';
        CtxShade.arc(Init.touchX, Init.touchY, Init.radius, 0, Math.PI * 2, true);
        CtxShade.fill();
        CtxShade.closePath();

        
        CtxImage.arc(Init.touchX, Init.touchY, Init.radius, 0, Math.PI * 2);
        CtxImage.fill();
        CtxImage.closePath();
      })
      // 触摸移动
      CanvasBox.addEventListener(isMobile ? 'touchmove' : 'mousemove', (e: any) => {
        e.preventDefault();
        if (!Init.image || !isMouseDown) {
          return;
        }
        let x = 0
        let y = 0
        const offset = e.target.getBoundingClientRect()
        if (isMobile) {
          x = e.touches[0].clientX - offset.left;
          y = e.touches[0].clientY - offset.top;
        } else {
          x = e.clientX - offset.left;
          y = e.clientY - offset.top;
        }

        CtxShade.beginPath();
        CtxShade.fillStyle = 'rgba(0,0,0,1)';
        CtxShade.arc(x, y, Init.radius, 0, Math.PI * 2, true);
        CtxShade.fill();
        CtxShade.closePath();

        
        CtxImage.arc(x, y, Init.radius, 0, Math.PI * 2);
        CtxImage.fill();
        CtxImage.closePath();
      })
      // 触摸结束
      CanvasBox.addEventListener(isMobile ? 'touchend' : 'mouseup', async (e: any) => {
        e.preventDefault();
        // CtxImage.closePath();
        isMouseDown = false
        if (Init.handleClip) {
          resolve({
            clip: async () => { 
              return await imageClip()
            }
          })
        } else { 
          resolve(await imageClip())
        }
      })
      // 裁剪
      let imageClip: any = () => {
        return new Promise((resolve, reject) => {
          try {
            CtxShade.clip();
            CtxImage.clip();
            CtxImage.drawImage(
              Img,
              0,
              0,
              Init.width,
              Img.height
            );
            const data = CtxImage.getImageData(0, 0, Init.width, Init.height).data
            let threshold = 0;
            let bOffset = 0
            let rOffset = 0
            let tOffset = Init.height
            let lOffset = Init.width
            for (let i = 0; i < Init.width; i++) {
              for (let j = 0; j < Init.height; j++) {
                let pos = (i + Init.width * j) * 4;
                if (data[pos + 3] > threshold) {
                  bOffset = Math.max(j, bOffset);
                  tOffset = Math.min(j, tOffset);
                  rOffset = Math.max(i, rOffset);
                  lOffset = Math.min(i, lOffset);
                }
              }
            }
            const base64 = (document.getElementById('CanvasImage') as any).toDataURL('image/png')
            const image = new Image()
            image.src = base64
            image.onload = () => {
              CanvasOut.setAttribute('style', `
                  width:${rOffset - lOffset}px;
                  height:${bOffset - tOffset}px;
                  position: absolute;
                  left: 10000px;
                  top: 0;
                `)
              CanvasOut.width = rOffset - lOffset
              CanvasOut.height = bOffset - tOffset
              CtxOut.drawImage(
                image,
                -lOffset,
                -tOffset,
                image.width,
                image.height
              )
              CtxOut.clip()
              let result = (document.getElementById('CanvasOut') as any).toDataURL('image/png')
              if (Init.el) {
                Init.el.removeChild(CanvasBox)
              } else {
                document.body.removeChild(CanvasBox)
              }
              CanvasBox = null
              CanvasImage = null
              CanvasShade = null
              CanvasOut = null
              CtxImage = null
              CtxOut = null
              CtxShade = null
              Img = null
              imageClip = null
              createShade = null
              fillShade = null
              resolve(result)
            }
          } catch { 
            resolve('')
          }
        })
      }
    }
    catch {
      resolve('')
    }
  })
}

/**
 * canvas多边形裁剪
 */
interface CanvasPolygonClip { 
  image: string // 图片路径
  el?: string // 挂载元素
  side?: number // 边数，不能小于3，边数越多可越接近圆，默认3
  radius?: number // 半径，点到中心距离，默认150
  rect?: boolean // 是否为正矩形，为true时，side失效，默认false
  rectWidth?: number // 正矩形的宽，没有或为0，默认取radius
  rectHeight?: number // 正矩形的长，没有或为0，默认取radius
  alpha?: number // 裁剪蒙层透明度，默认0.6
  inner?: boolean // 是否为内多边形，默认false
  innerTimes?: number // 内多边形时，靠内的点半径为radius*innerTimes， 默认0.5
  width?: number // 宽度，仅pc端有效，pc默认50vw，移动端固定全屏
  height?: number // 高度，仅pc端有效，pc默认100vh，移动端固定全屏
  rotate?: boolean // 是否可旋转，默认true
}
interface Init { 
  el: any
  image: string
  side: number
  radius: number
  alpha: number
  rect: boolean
  rectWidth: number
  rectHeight: number
  inner: boolean
  innerTimes: number
  width: number
  height: number
  clipX: number
  clipY: number
  imageWidth: number
  imageHeight: number
  imageRotate: number
  rotate: boolean
}
export const CanvasPolygonClip = (config:CanvasPolygonClip) => {
  return new Promise(async (resolve, reject) => {
    try {
      if (!config?.image) {
        console.error('prototype: image is empty!')
        resolve('')
      }
      if (config?.side && config?.side < 3) {
        console.error('prototype: side less than 3!')
        resolve('')
      }
      // 数据初始化
      const isMobile = /mobile|android|webos|iphone|ipad|phone/i.test(window.navigator.userAgent.toLowerCase())
      let Init: Init = {
        el: document.getElementById(config.el as any),
        image: config.image,
        side: config.side || 3,
        radius: config?.radius || 150,
        alpha: config?.alpha || 0.6,
        rect: config?.rect || false,
        rectWidth: config?.rectWidth || 150,
        rectHeight: config?.rectHeight || 150,
        inner: config?.inner || false,
        innerTimes: config.innerTimes || 0.4,
        width:  isMobile ? window.innerWidth : config?.width || window.innerWidth * 0.5,
        height: isMobile ? window.innerHeight: config?.height || window.innerHeight,
        rotate: config?.rotate === false ? false : true,
        clipX: 0,
        clipY: 0,
        imageWidth: 0,
        imageHeight: 0,
        imageRotate: 0
      }
      // DOM生成
      // 装画布的盒子
      let CanvasBox: any = document.createElement('div')
      CanvasBox.setAttribute('style', `
        width:${Init.width}px;
        height:${Init.height}px;
        overflow: hidden;
        position: ${Init?.el ? 'relative' : 'fixed'};
        left: ${Init?.el ? 'unset' : '0'};
        top: ${Init?.el ? 'unset' : '0'};
        margin: 0 auto
      `)

      // 图片
      let Img: any = new Image()
      Img.src = typeof Init.image === 'object' ? await FileToBase64(Init.image) as any : Init.image
      Img.setAttribute('style', `
        display: block;
        position:absolute;
        left:0;
        top:0
      `)
      Img.onload = () => {
        const w = Img.width
        const h = Img.height
        Img.width = Init.width
        Img.height = Init.width * h / w
        Init.imageWidth = Init.width
        Init.imageHeight = Init.width * h / w
      }


      // 主画布创建，裁剪层
      let CanvasImage: any = document.createElement('canvas')
      CanvasImage.width = Init.width
      CanvasImage.height = Init.height
      CanvasImage.setAttribute('style', `
        width:${Init.width}px;
        height:${Init.height}px;
        position: absolute;
        left: 10000px;
        top: 0;
      `)
      CanvasImage.id = "CanvasImage"
      let CtxImage: any = CanvasImage.getContext('2d');
      CtxImage.translate(Init.width / 2, Init.height / 2)

      // 遮罩层，涂抹层
      let CanvasShade: any = document.createElement('canvas')
      CanvasShade.width = Init.width
      CanvasShade.height = Init.height
      CanvasShade.setAttribute('style', `
        width:${Init.width}px;
        height:${Init.height}px;
        position: absolute;
        left: 0;
        top: 0;
        z-index: 9999
      `)
      CanvasShade.id = "CanvasShade"
      let CtxShade: any = CanvasShade.getContext('2d');
      CtxShade.translate(Init.width / 2, Init.height / 2)


      // 输出层，保证输出的图像大小和涂抹的一样
      let CanvasOut: any = document.createElement('canvas')
      CanvasOut.id = "CanvasOut"
      let CtxOut: any = CanvasOut.getContext('2d');

      CtxShade.save();
      CtxImage.save();

      CanvasBox.appendChild(Img)
      CanvasBox.appendChild(CanvasImage)
      CanvasBox.appendChild(CanvasShade)
      CanvasBox.appendChild(CanvasOut)
      if (Init.el) {
        Init.el.appendChild(CanvasBox)
      } else {
        document.body.appendChild(CanvasBox)
      }
      // 裁剪的形状，主要函数
      let polygon: any = (ctx:any, bool:Boolean, conf:any) => {
        return new Promise(async (resolve, reject) => {
          let x = conf && conf.x || 0;  // 中心点x坐标，默认0
          let y = conf && conf.y || 0;  // 中心点y坐标，默认0
          let num = conf && conf.num || 3;   // 图形边的个数，默认3
          let r = conf && conf.r || 100;   // 图形的半径
          let inner = conf && conf.inner || false; // 是否矩形
          let rect = conf && conf.rect || false; // 是否矩形
          let rectW = conf && conf.rectW || r; // 矩形宽，默认半径
          let rectH = conf && conf.rectH || r; // 矩形高，默认半径
          let width = conf && conf.width || 2; // 边线宽度
          let strokeStyle = conf && conf.strokeStyle || false; // 边线颜色
          // 画正矩形
          if (rect) {
            ctx.beginPath();
            ctx.rect(x - rectW / 2- Init.width / 2, y - rectH / 2- Init.height / 2, rectW, rectH);
            ctx.closePath();
          } else { // 其他形状
              // 开始路径
              ctx.beginPath();
              let startX = x - r * Math.sin(2*Math.PI*0/num);
              let startY = y - r * Math.cos(2*Math.PI*0/num);
              ctx.moveTo(startX- Init.width / 2, startY- Init.height / 2);
              for(let i = 1; i <= num; i++) {
                if (inner) {
                  if(i%2 === 0) {
                    let newX = x - r * Math.sin(2*Math.PI*i/num);
                    let newY = y - r * Math.cos(2*Math.PI*i/num);
                    ctx.lineTo(newX- Init.width / 2, newY- Init.height / 2);							
                  } else {
                    let newX1 = x - Math.ceil(r * Init.innerTimes) * Math.sin(2*Math.PI*i/num);
                    let newY1 = y - Math.ceil(r * Init.innerTimes) * Math.cos(2*Math.PI*i/num);
                    ctx.lineTo(newX1- Init.width / 2, newY1- Init.height / 2);							
                  }
                } else {
                  let newX = x - r * Math.sin(2*Math.PI*i/num);
                  let newY = y - r * Math.cos(2*Math.PI*i/num);
                  ctx.lineTo(newX- Init.width / 2, newY- Init.height / 2);	
                }

              }
              // 路径闭合
              ctx.closePath();
          }

          // 图片边框
          if(strokeStyle) {
              ctx.strokeStyle = strokeStyle;
              ctx.lineWidth = 1;
              ctx.lineJoin = 'round';
              ctx.stroke();
          }	
          // 蒙层，反向裁剪
          if (!bool) {
            ctx.fill();
            ctx.globalCompositeOperation = 'source-out';
            ctx.beginPath();
            ctx.rect(0- Init.width / 2, 0- Init.height / 2, Init.width, Init.height);
            ctx.closePath();
            // ctx.setGlobalAlpha(Number(this.globalAlpha));
            // ctx.setFillStyle = '#000';
            ctx.fillStyle = 'rgba(0,0,0,'+Number(Init.alpha)+')';
            ctx.fill();
            resolve(true)
          }
          // 裁剪
          if (bool) {
            let img = new Image()
            img.src = (typeof Init.image === 'object' ? await FileToBase64(Init.image) : Init.image) as any
            let data = await imageClip(img)
            resolve(data)
          }
        })
      }

      // 蒙层
      await polygon(CtxShade, false, {
				x: Init.width / 2,
				y: Init.height / 2,
				num: Init.side,
				r: Init.radius,
				rect: Init.rect,
				rectW: Init.rectWidth,
				rectH: Init.rectHeight,
				inner: Init.inner
      });
      // 裁剪
      resolve({
        clip: async () => { 
          return await polygon(CtxImage, true, {
            x: Init.width / 2,
            y: Init.height / 2,
            num: Init.side,
            r: Init.radius,
            rect: Init.rect,
            rectW: Init.rectWidth,
            rectH: Init.rectHeight,
            inner: Init.inner
          });
        }
      })
      let touches: any = []
      let moveTouches: any = [] // 拖拽会用的，保存xy
      let lenTouches: any = [] // 移动端-缩放会用的，保存宽高
      let rotateTouches:any = 0 // 移动端-旋转会用的，保存旋转角度
      let initLen = 0 // 缩放会用的，初始两点距离
      if (isMobile) { // 移动端触摸
        // 触摸开始
        CanvasBox.addEventListener('touchstart', (e: any) => {
          e.preventDefault();
          if (!Init.image) {
            return;
          }
          touches = []
          for (const item of e.touches) { // 获取指点信息
            let list = [item.clientX, item.clientY]
            touches.push(list)
          }
          if (touches.length === 2) { 
            initLen = Math.abs(touches[0][0] - touches[1][0])
          }
          if (moveTouches.length === 2) { 
            Init.clipX = moveTouches[0] || Init.clipX
            Init.clipY = moveTouches[1] || Init.clipY
            moveTouches = []
          }
          if (lenTouches.length === 2) { 
            Init.imageWidth = lenTouches[0] || Init.imageWidth
            Init.imageHeight = lenTouches[1] || Init.imageHeight
            lenTouches = []
          }
        })
        // 触摸移动
        CanvasBox.addEventListener('touchmove', (e: any) => {
          e.preventDefault();
          if (!Init.image) {
            return;
          }
          if (touches.length === 1) { // 拖拽
            const moveX = e.touches[0].clientX - touches[0][0]
            const moveY = e.touches[0].clientY - touches[0][1]
            Img.style.left = moveX + Init.clipX+'px'
            Img.style.top = moveY + Init.clipY + 'px'
            moveTouches=[moveX + Init.clipX,moveY + Init.clipY]
          }
          if (touches.length === 2) { // 缩放
            const len = Math.abs(e.touches[0].clientX - e.touches[1].clientX)
            const r = (len - initLen) * 2
            const w = r + Init.imageWidth
            const h = (w * Init.imageHeight / Init.imageWidth)
            Img.style.width = w +'px'
            Img.style.height = h + 'px'
            lenTouches = [w, h]
            
            Img.style.left = -r/2 + Init.clipX+'px'
            Img.style.top = -(h - Init.imageHeight) / 2 + Init.clipY + 'px'
            moveTouches=[-r/2 + Init.clipX, -(h - Init.imageHeight) / 2 + Init.clipY]
          }
          if (touches.length === 3 && Init.rotate) { // 旋转 
            const origin = [Init.width / 2, Init.height / 2] // 中心点
            const firstDot = touches[0] // 以第一个点为主计算旋转角度
            const nextDot = [e.touches[0].clientX, e.touches[0].clientY] //
            const rotate = getAngle([firstDot[0] - origin[0], firstDot[1] - origin[1]], [nextDot[0] - origin[0], nextDot[1] - origin[1]])
            if (rotate > 0 && rotate < 180) { // 右转
              rotateTouches = Init.imageRotate + rotate
            } else { // 左转
              rotateTouches = Init.imageRotate - (360-rotate)
            }
            
            Img.style.transform=`rotate(${rotateTouches}deg)`
          }
        })
        // 触摸结束
        CanvasBox.addEventListener('touchend', (e: any) => {
          e.preventDefault();
          touches = []
          Init.clipX = moveTouches[0] || Init.clipX
          Init.clipY = moveTouches[1] || Init.clipY
          Init.imageWidth = lenTouches[0] || Init.imageWidth
          Init.imageHeight = lenTouches[1] || Init.imageHeight
          Init.imageRotate = rotateTouches
          moveTouches = []
          lenTouches = []
          initLen = 0
          // rotateTouches = 0
          if (e.touches.length === 1) { // 还有一个手指
            for (const item of e.touches) {
              let list = [item.clientX, item.clientY]
              touches.push(list)
            }
          }
        })
      } else {// PC端鼠标事件 
        // PC端滚轮事件，放大缩小
        CanvasBox.addEventListener('wheel', (e: any) => {
          e.preventDefault()
          const step = -e.deltaY 
          const w = step + Init.imageWidth
          const h = (w * Init.imageHeight / Init.imageWidth)
          Img.style.width = w +'px'
          Img.style.height = h + 'px'
          
          Img.style.left = -step/2 + Init.clipX+'px'
          Img.style.top = -(h - Init.imageHeight) / 2 + Init.clipY + 'px'
          moveTouches = [-step / 2 + Init.clipX, -(h - Init.imageHeight) / 2 + Init.clipY]

          Init.clipX = -step/ 2 + Init.clipX
          Init.clipY = -(h - Init.imageHeight) / 2 + Init.clipY
          Init.imageWidth = w
          Init.imageHeight = h
        })
        // 取消默认鼠标右键事件
        CanvasBox.addEventListener("contextmenu", (e:any) => {
            e.preventDefault();
        });
        // pc鼠标按下
        let isMouseDown = false
        let buttonType = 0 // 0 左键按下，1滚轮按下，2右键按下
        CanvasBox.addEventListener('mousedown', (e: any) => {
          isMouseDown = true
          e.preventDefault();
          buttonType = e.button
          if (!Init.image) {
            return;
          }
          const offset = e.target.getBoundingClientRect()
          touches = [e.clientX - offset.left, e.clientY - offset.top]
          
        })
        // pc鼠标移动
        CanvasBox.addEventListener('mousemove', (e: any) => {
          e.preventDefault();
          if (!Init.image || !isMouseDown) {
            return;
          }
          const offset = e.target.getBoundingClientRect()
          let x = e.clientX - offset.left;
          let y = e.clientY - offset.top;
          const moveX = x - touches[0]
          const moveY = y - touches[1]
          
          if (buttonType === 0) { // 左键按下，移动
            Img.style.left = moveX + Init.clipX+'px'
            Img.style.top = moveY + Init.clipY + 'px'
            moveTouches = [moveX + Init.clipX, moveY + Init.clipY]
            
          }
          if (buttonType === 2 && Init.rotate) { // 右键按下，旋转
            rotateTouches = Init.imageRotate + moveY / 2
            Img.style.transform = `rotate(${rotateTouches}deg)`
            
          }

          
        })
        // pc鼠标放开
        CanvasBox.addEventListener('mouseup', async (e: any) => {
          e.preventDefault();
          isMouseDown = false
          Init.clipX = moveTouches[0] || Init.clipX
          Init.clipY = moveTouches[1] || Init.clipY
          Init.imageRotate = rotateTouches || Init.imageRotate
          buttonType = 0
          moveTouches = []
          touches = []
        })
      }
      const getAngle = ([x1, y1]:any, [x2, y2]:any) => {
        const dot = x1 * x2 + y1 * y2
        const det = x1 * y2 - y1 * x2
        const angle = Math.atan2(det, dot) / Math.PI * 180
        return Math.round(angle + 360) % 360
      }
      
      // 裁剪
      let imageClip: any = (img:any) => {
        return new Promise((resolve, reject) => {
          try {
            CtxShade.clip();
            CtxImage.clip();
            let x = Math.ceil(Init.clipX - Init.width / 2)
            let y = Math.ceil(Init.clipY - Init.height / 2)
            if (Init.imageRotate) { // 旋转
              CtxImage.rotate(Init.imageRotate * Math.PI / 180);
            }
            CtxImage.drawImage(
              img,
              x,
              y,
              Math.ceil(Init.imageWidth),
              Math.ceil(Init.imageHeight)
            );
            const data = CtxImage.getImageData(0, 0, Init.width, Init.height).data
            let threshold = 0;
            let bOffset = 0
            let rOffset = 0
            let tOffset = Init.height
            let lOffset = Init.width
            for (let i = 0; i < Init.width; i++) {
              for (let j = 0; j < Init.height; j++) {
                let pos = (i + Init.width * j) * 4;
                if (data[pos + 3] > threshold) {
                  bOffset = Math.max(j, bOffset);
                  tOffset = Math.min(j, tOffset);
                  rOffset = Math.max(i, rOffset);
                  lOffset = Math.min(i, lOffset);
                }
              }
            }
            const base64 = (document.getElementById('CanvasImage') as any).toDataURL('image/png')
            const image = new Image()
            image.src = base64
            image.onload = () => {
              CanvasOut.setAttribute('style', `
                  width:${rOffset - lOffset}px;
                  height:${bOffset - tOffset}px;
                  position: absolute;
                  left: 10000px;
                  top: 0;
                `)
              CanvasOut.width = rOffset - lOffset
              CanvasOut.height = bOffset - tOffset
              CtxOut.drawImage(
                image,
                -lOffset,
                -tOffset,
                image.width,
                image.height
              )
              CtxOut.clip()
              let result = (document.getElementById('CanvasOut') as any).toDataURL('image/png')
              if (Init.el) {
                Init.el.removeChild(CanvasBox)
              } else {
                document.body.removeChild(CanvasBox)
              }
              // Init = null
              CanvasBox = null
              CanvasImage = null
              CanvasShade = null
              CanvasOut = null
              CtxImage = null
              CtxOut = null
              CtxShade = null
              rotateTouches = 0
              touches = []
              moveTouches = []
              lenTouches = []
              initLen = 0
              Img = null
              imageClip = null
              resolve(result)
            }
          } catch { 
            resolve('')
          }
        })
      }
    }
    catch {
      resolve('')
    }
  })
} 




/****************************************************************************** */
/****************************************************************************** */
/*****************************  判断类  *************************************** */
/****************************************************************************** */
/****************************************************************************** */
/****************************************************************************** */

// 是否是手机
export const IsPhone = () => {
  return /mobile|android|webos|iphone|ipad|phone/i.test(window.navigator.userAgent.toLowerCase())
};
// 是否是object
export const IsObject = (val: any) => {
  // return val != null && typeof val === 'object' && Array.isArray(val) === false;
  return Object.prototype.toString.call(val) === '[object Object]';
};
// 是否是数组
export const IsArray = (val: any) => {
  return Object.prototype.toString.call(val) == '[object Array]';
};
// 是否是数字
export const IsNumber = (val: any) => {
  return Object.prototype.toString.call(val) == '[object Number]';
};
// 是否是字符串
export const IsString = (val: any) => {
  return Object.prototype.toString.call(val) == '[object String]';
};
// 是否是时间
export const IsDate = (val: any) => {
  return Object.prototype.toString.call(val) == '[object Date]';
};
// 是否是Boolean类型
export const IsBoolean = (val: any) => {
  return Object.prototype.toString.call(val) == '[object Boolean]';
};
// 是否是函数
export const IsFunction = (val: any) => {
  return Object.prototype.toString.call(val) == '[object Function]';
};
// 是否为null
export const IsNull = (val: any) => {
  return Object.prototype.toString.call(val) == '[object Null]';
};
// 是否为undefind
export const IsUndefined = (val: any) => {
  return Object.prototype.toString.call(val) == '[object Undefined]';
};
// 是否为false
export const IsFalse = (val: any) => {
  if (['', 'null', 'undefined', 0, false, undefined, null, NaN].includes(val)) return true;
  return false;
};
// 是否为true
export const IsTrue = (val: any) => {
  return !IsFalse(val);
};





export default {
  Mobile, StudioCamera, Email, Digit, DigitLetter, IdCard, Chinese, CnEnNuLine, English, CapitalEnglish, Ipv4, Url, PostCode, SocialCreditCode,
  IsMobile, IsStudioCamera, IsEmail, IsDigit, IsDigitLetter, IsIdCard, IsChinese, IsCnEnNuLine, IsEnglish, IsCapitalEnglish, IsIpv4, IsUrl, IsPostCode, IsSocialCreditCode,
  sessions, local,
  DateFormat, NumberDot, ReplaceStar, NumberIsFloat, OutScreenFileChoose, NumberWan, FileToBase64, DeleteEmptyObj, FileDownload, Base64toBlob, WebImageToBase64, XMLHttpRequest, Copy,DeepClone,Debounce,Throttle,
  CanvasImageCompose, CanvasDaubClip, CanvasPolygonClip, 
  IsObject, IsArray, IsNumber, IsString, IsDate, IsBoolean, IsFunction, IsNull, IsUndefined, IsFalse, IsTrue, IsPhone
}